; enter IA-32e mode to test compatibility and 64bit mode
; linear address [0, 0x200000) will be mapped as it is
  format mz
  include "../common/pminc.asm"
  include "../common/libx86.asm"

  ; page structs are begin here
  PML4_PHY_ADDR = 0x400000
  PDP_PHY_ADDR  = 0x401000
  PD_PHY_ADDR  = 0x402000
  PT_PHY_ADDR  = 0x403000
  
  .GDT
  DUMMY_DESC
  normal  DESC  0,      0xffff,     SDT_DATA_RW
  vedio   DESC  0x200000,0x3000-1,  SDT_DATA_RW
  .block g0init
    gtc   MZDESC main,  0xffff,     SDT_CODE_EONLY
    gc32  MZDESC c32,   c32.limit,  SDT_CODE_EONLY+SDT_DEFAULT32
    gc64  MZDESC c64,   c64.limit,  SDT_CODE_RE+SDT_L64
    gda   MZDESC dat,   dat.limit,  SDT_DATA_RW ; 
    gstk  MZDESC stk,   stk.limit,  SDT_DATA_RW+SDT_DEFAULT32
  .endb
  gpgs    DESC PML4_PHY_ADDR,-1,SDT_DATA_RW ; 1MB at GPAGE_STRUCT_PHY_ADDR
  .data stk
  rb 256 
  
  .code64 c64
at_pmode64:
  lea rsp,[rip+stk64_top]
  mov rax,[rip+8]
  
  ; there are no jmp sel:off under 64 bit mode, but indirect jump and retf iret etc are still functioning
exit_pmode64:
  push gc32.sel
  push back_at_pmode32
  retf

stk64:
  db 256 dup(0)
stk64_top:

  .code32 c32
at_pmode32:
  mov ax,gstk.sel
  mov ss,ax
  mov esp,stk.size
  mov ax,gda.sel
  mov ds,ax
  mov ax,gpgs.sel
  mov es,ax

init_pg_struct:
  mov esi,pml4seg
  mov edi,0
  mov ecx,pseg_end-pml4seg
  rep movsb

  mov ax,vedio.sel
  mov es,ax

  ; start using PG and PAE and set cr3
  SetCr4PAEbit
  mov eax,PML4_PHY_ADDR
  mov cr3,eax
  SetEferLMEbit
  SetCr0Pgbit
  
  ; in 64 bit mode, segment base are from 0 has nothing to do with 
  ; jmp gc64.sel:at_pmode64 will not work because 

  mov eax,[pm64_entry]
  push gc64.sel
  push eax
  retf

back_at_pmode32:
  
  ; disable PAE and PG LME
  ClrCr0Pgbit
  ClrEferLMEbit
  ; enter legacy Paging mode with PAE on
  ;SetCr0Pgbit
  jmp @f  ; the document said that it's necessary
@@:
  
  ClrCr4PAEbit
  mov eax,0
  mov cr3,eax
  jmp gtc.sel:exit_pmode

; ds:esi =>src string
; es:edi =>dst buffer
direct_draw_str:
  mov     ah,4fh
@@:
  lodsb
  test    al,al
  jz      @F
  stosw
  jmp     @B
@@:
  ret

  .data dat
pml4seg:
  pml0 PTE64 PDP_PHY_ADDR, PE_PRESENT

align 4096
pdpseg:
  pdp0 PTE64 PD_PHY_ADDR, PE_PRESENT
  pdp1 PTE64 
  pdp2 PTE64 
  pdp3 PTE64 

align 4096
pdseg:
  pd0 PTE64 0,          PE_PS+PE_PRESENT
  pd1 PTE64 PT_PHY_ADDR, PE_PRESENT
  ; use align to make the physical address fall in specified address
align 4096
ptseg:
  PTE64   0xb8000,PE_PRESENT
  PTE64   0xb9000,PE_PRESENT
  PTE64   0xba000,PE_PRESENT
pseg_end:

serr_cpuid db "CPUID is not supported by this CPU!",0x0d,0x0a,'$'
serr_pae db "PAE is not supported by this CPU!",0x0d,0x0a,'$'
serr_lmode db "x86-64 Long Mode is not supported by this CPU!",0x0d,0x0a,'$'
sok_pae db "PAE FEATURE is supported!",0x0d,0x0a,'$'
sok_lmode db "LONG-MODE FEATURE is supported!",0x0d,0x0a,'$'

VgdtR XDTR GDTSEG.limit, 0
orgSP dw 0
orgSS dw 0

s1  db "This is the first Line!", 0
s2  db "This is the second Line!", 0
s3  db "This is the third Line!", 0

pm64_entry dd 0

  .code main
start:
  mov ax,dat
  mov ds,ax
  IsCpuSupportCPUID
  test eax,eax
  jz exit_prog.err_cpuid
  IsCpuSupportPAE
  test eax,eax
  jz exit_prog.err_pae

  mov dx,sok_pae
  iDos21 9

  IsCpuSupportLmode
  test eax,eax
  jnz continue_prog
warns:
  mov dx,serr_lmode
  iDos21 9

continue_prog:
  mov dx,sok_lmode
  iDos21 9

enter_pmode:
  mov eax,c64
  shl eax,4
  mov [pm64_entry],eax

  mov eax,GDTSEG
  shl eax,4
  shld edx,eax,16      ;seg*16
  mov [VgdtR.base_lo],ax
  mov [VgdtR.base_hi],dx  ;init GDTR

  ;init GDT descriptors
  mov ax,GDTSEG
  mov ds,ax
  mov si,g0init
  mov cx,g0init.items
  call ..init_xdt
  
  mov ax,dat
  mov ds,ax
  mov [orgSP],sp
  mov [orgSS],ss

  cli        
  EnableA20 
  lgdt fword [VgdtR]
  SetCr0Pebit
  jmp fword gc32.sel:at_pmode32

exit_pmode:

  mov ax,normal.sel
  mov ds,ax
  mov es,ax
  mov ss,ax
  mov fs,ax
  mov gs,ax
  cli
	ClrCr0Pebit
  jmp dword main:back_at_real_mode

back_at_real_mode:
  mov ax,dat
  mov ds,ax
  lss sp,dword [orgSP]
  DisableA20
  sti

exit_prog:
  mov ax,0x4c00
  int 21h

.err_cpuid:
  mov dx,serr_cpuid
  iDos21	9
  jmp exit_prog

.err_pae:
  mov dx,serr_pae
  iDos21	9
  jmp exit_prog

  init_xdt_proc
.end main:start

